﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.Rpc;
using Wicture.DbRESTFul.Rpc.Client;
using Wicture.QPZS.EPC.Models;
using Wicture.QPZS.EPC.Repository;

namespace Wicture.QPZS.EPC.Services
{
    public class QueryProcessor
    {
        private QueryLimitRule ruleConfig;
        private QueryProcessor()
        {
            ruleConfig = ConfigurationManager.Settings.Document.ContainsKey("QueryLimitRule")
               ? ConfigurationManager.Settings.GetConfig<QueryLimitRule>("QueryLimitRule")
               : new QueryLimitRule();
        }
        public static QueryProcessor Instance = new QueryProcessor();

        public void CheckSearchPartTimes(IDbRESTFulRepository repository, string brandCode, string keyword)
        {
            if (ruleConfig.PartSearchTimes)
            {
                RpcClient rpcClient = new RpcClient();
                rpcClient.Call<JObject>("Wicture.QPZS.Member", "Update_and_check_query_count", new { brandCode, keyword }, repository.HttpContext, repository.CurrentUser);
            }
        }

        public void CheckSearhPartCounts(IDbRESTFulRepository repository,int times, string brandCode)
        {
            if (ruleConfig.PartSearchCount)
            {
                RpcClient rpcClient = new RpcClient();
                rpcClient.Call<JObject>("Wicture.QPZS.Member", "Update_and_check_query_times", new { times, brandCode }, repository.HttpContext, repository.CurrentUser);
            }
        }

        public void BuildPartSearchResult(IDbRESTFulRepository repository, JObject data)
        {
            if(data == null)
            {
                return;
            }
            data["parts"].ForEach(q =>
            {
                var tmpCost = q["cost"]?.ToString();
                if (!HasPrice(tmpCost)) q["cost"] = null;
                var tmpPrice = q["price"]?.ToString();
                if (!HasPrice(tmpPrice)) q["price"] = null;
            });
            if (ruleConfig.PartPrice)
            {
                var brandPricePackage = GetMemberBrandPricePackage(repository);
                if (!brandPricePackage.allBrand)
                {
                    HashSet<string> brandCodes = brandPricePackage.brandCodes;
                    data["parts"].ForEach(q =>
                    {
                        BuildPriceObject(q, brandCodes);
                    });
                }

            }
        }

        public void BuildPartListResult(IDbRESTFulRepository repository, JArray data, string brandCode)
        {
            if (data == null || data.Count == 0)
            {
                return;
            }
            data.ForEach(q =>
            {
                var tmpCost = q["cost"]?.ToString();
                if (!HasPrice(tmpCost)) q["cost"] = null;
                var tmpPrice = q["price"]?.ToString();
                if (!HasPrice(tmpPrice)) q["price"] = null;
            });
            if (ruleConfig.PartPrice)
            {
                if (!MemberHasPricePackage(repository, brandCode))
                {
                    data.ForEach(q =>
                    {
                        if (!string.IsNullOrEmpty(q["cost"]?.ToString())) q["cost"] = -1;
                    });
                }
            }
        }

        public void BuildPartDetailResult(IDbRESTFulRepository repository, JObject data, string brandCode)
        {
            if (data == null)
            {
                return;
            }
            var tmpCost = data["cost"]?.ToString();
            if (!HasPrice(tmpCost)) data["cost"] = null;
            var tmpPrice = data["price"]?.ToString();
            if (!HasPrice(tmpPrice)) data["price"] = null;

            var replacements = data.Value<JArray>("replacements");
            if (replacements != null)
            {
                replacements.ForEach(q =>
                {
                    if (!HasPrice(q["cost"]?.ToString())) q["cost"] = null;
                    if (!HasPrice(q["price"]?.ToString())) q["price"] = null;
                });
            }
            var partAttributes = data.Value<JArray>("partAttributes");
            if (partAttributes != null)
            {
                partAttributes.ForEach(q =>
                {
                    if (!HasPrice(q["cost"]?.ToString())) q["cost"] = null;
                    if (!HasPrice(q["price"]?.ToString())) q["price"] = null;
                });
            }
            if (ruleConfig.PartPrice)
            {
                var brandPricePackage = GetMemberBrandPricePackage(repository);
                if (!brandPricePackage.allBrand)
                {
                    HashSet<string> brandCodes = brandPricePackage.brandCodes;
                    BuildPriceObject(data, brandCodes, brandCode);
                    if (replacements != null)
                    {
                        replacements.ForEach(q => BuildPriceObject(q, brandCodes));
                    }
                    if (partAttributes != null)
                    {
                        partAttributes.ForEach(q => BuildPriceObject(q, brandCodes));
                    }
                }
            }
        }
        private void BuildPriceObject(JObject priceObject, HashSet<string> brands, string brandCode = "")
        {
            if (string.IsNullOrEmpty(brandCode))
            {
                brandCode = priceObject["brandCode"]?.ToString();
            }
            if (!string.IsNullOrEmpty(priceObject["cost"]?.ToString()) && !brands.Contains(brandCode))
            {
                priceObject["cost"] = -1;
            }
        }
        private void BuildPriceObject(JToken priceObject, HashSet<string> brands, string brandCode = "")
        {
            if (string.IsNullOrEmpty(brandCode))
            {
                brandCode = priceObject["brandCode"]?.ToString();
            }
            if (!string.IsNullOrEmpty(priceObject["cost"]?.ToString()) && !brands.Contains(brandCode))
            {
                priceObject["cost"] = -1;
            }
        }

        private bool HasPrice(string price)
        {
            if(price == null || price.ToDecimal() == 0)
            {
                return false;
            }
            return true;
        }

        public void PartListPreProcess(EpcRepository repository, string brandCode)
        {
            if (ruleConfig.SubGroupCount)
            {
                repository.Call<JObject>("Wicture.QPZS.Member", "DecreaseModelQueryCount", new { brandCode });
            }
        }

        public void BuildVinResult(RpcRepository repository, string brandCode, string vin, string modelName)
        {
            if (ruleConfig.VinQueryTimes)
            {
                repository.Call<JObject>("Wicture.QPZS.Member", "Decrease_VinQueryCount", new { vin, brandCode, modelName });
            }
        }


        private bool MemberHasPricePackage(IDbRESTFulRepository repository, string brandCode)
        {
            RpcClient rpcClient = new RpcClient();
            var ruleData = rpcClient.Call<JObject>("Wicture.QPZS.Member", "Member_Has_Price_package", new { brandCode }, repository.HttpContext, repository.CurrentUser).data;
            var visible = ruleData.Value<int>("visible");
            if (visible == 0)
            {
                return false;
            }
            return true;
        }

        private MemberBrandPricePackage GetMemberBrandPricePackage(IDbRESTFulRepository repository)
        {
            RpcClient rpcClient = new RpcClient();
            var data = rpcClient.Call<MemberBrandPricePackage>("Wicture.QPZS.Member", "Member_Price_Brands", null, repository.HttpContext, repository.CurrentUser).data;
            return data;
        }

        public void CheckBrandPartCounts(IDbRESTFulRepository repository)
        {
            if (ruleConfig.BrandPartCount)
            {
                RpcClient rpcClient = new RpcClient();
                rpcClient.Call<JObject>("Wicture.QPZS.Member", "Decrease_BrandQueryCount", new { }, repository.HttpContext, repository.CurrentUser);
            }
        }

        public void CheckMaintainCounts(IDbRESTFulRepository repository, string vin, string brandCode, string model)
        {
            if (ruleConfig.MaintainCount)
            {
                RpcClient rpcClient = new RpcClient();
                rpcClient.Call<JObject>("Wicture.QPZS.Member", "DecreaseMaintainCount", new { vin, brandCode, model }, repository.HttpContext, repository.CurrentUser);
            }
        }

        public List<VinPart> QueryVinParts(IDbRESTFulRepository repository, string vin, string brandCode)
        {
            try
            {
                string token = repository.HttpContext.Request.Headers["Authorization"].ToString();
                HttpClient httpClient = new HttpClient();
                httpClient.DefaultRequestHeaders.Add("Authorization", token);
                HttpResponseMessage resp = httpClient
                    .GetAsync($"{(repository.HttpContext.Request.IsHttps ? "https://" : "http://")}{repository.HttpContext.Request.Host}/api/epc/vinparts?vin={vin}&brandCode={brandCode}").Result;
                resp.EnsureSuccessStatusCode();
                string dataString = resp.Content.ReadAsStringAsync().Result;
                var tt = JsonConvert.DeserializeObject<VinPartResult>(dataString);
                return tt.data;
            }
            catch
            {
                return new List<VinPart>();
            }  
        }
    }

    public class QueryLimitRule
    {
        public bool TurnOff { get; set; }

        public bool PartSearchTimes { get { return !TurnOff; } }
        public bool PartSearchCount { get { return !TurnOff; } }
        public bool PartPrice { get { return !TurnOff; } }
        public bool VinQueryTimes { get { return !TurnOff; } }
        public bool BrandPartCount { get { return !TurnOff; } }
        public bool MaintainCount { get { return !TurnOff; } }
        public bool SubGroupCount
        {
            get { return !TurnOff; }
        }
    }
}
